package com.adee.gulimall.search.service.imp;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.util.ObjectBuilder;
import com.adee.gulimall.common.to.es.SkuESModel;
import com.adee.gulimall.search.constant.EsConstant;
import com.adee.gulimall.search.service.MallSearchService;
import com.adee.gulimall.search.vo.SearchParam;
import com.adee.gulimall.search.vo.SearchParam4es;
import com.adee.gulimall.search.vo.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MallSearchServiceImpl implements MallSearchService {
    @Autowired
    private ElasticsearchClient client;

    /**
     * 整体检索、过滤、排序、分页、高亮、聚合
     * get gulimall_product/_search
     * {
     *   "query": {
     *     "bool": {
     *       "must": [
     *         {
     *           "match": {
     *             "skuTitle": "华为 apple 小米"
     *           }
     *         }
     *       ],
     *       "filter": [
     *         {
     *           "term": {
     *             "catalogId": 225
     *           }
     *         },
     *         {
     *           "terms": {
     *             "brandId": [
     *               9,
     *               10,
     *               11,
     *               12,
     *               15
     *             ]
     *           }
     *         },
     *         {
     *           "nested": {
     *             "path": "attrs",
     *             "query": {
     *               "bool": {
     *                 "must": [
     *                   {
     *                     "term": {
     *                       "attrs.attrId": {
     *                         "value": 15
     *                       }
     *                     }
     *                   },
     *                   {
     *                     "terms": {
     *                       "attrs.attrValue": [
     *                         "以官网信息为准",
     *                         "高通(Qualcomm)"
     *                       ]
     *                     }
     *                   }
     *                 ]
     *               }
     *             }
     *           }
     *         },
     *         {
     *           "term": {
     *             "hasStock": false
     *           }
     *         },
     *         {
     *           "range": {
     *             "skuPrice": {
     *               "gte": 0,
     *               "lte": 8000
     *             }
     *           }
     *         }
     *       ]
     *     }
     *   },
     *   "sort": [
     *     {
     *       "skuPrice": {
     *         "order": "desc"
     *       }
     *     }
     *   ],
     *   "from": 0,
     *   "size": 2,
     *   "highlight": {
     *     "fields": {
     *       "skuTitle":{}
     *     },
     *     "pre_tags": "<b style='color:red'>",
     *     "post_tags": "</b>"
     *   },
     *   "aggs": {
     *     "brand_agg": {
     *       "terms": {
     *         "field": "brandId",
     *         "size": 10
     *       },
     *       "aggs":{
     *         "brand_name_agg":{
     *           "terms": {
     *             "field": "brandName",
     *             "size": 10
     *           }
     *         }
     *       }
     *     },
     *     "catalog_agg":{
     *       "terms": {
     *         "field": "catalogId",
     *         "size": 10
     *       },
     *       "aggs":{
     *         "catalog_name_agg":{
     *           "terms": {
     *             "field": "catalogName",
     *             "size": 10
     *           }
     *         }
     *       }
     *     },
     *     "attr_agg":{
     *       "nested": {
     *         "path": "attrs"
     *       },
     *       "aggs":{
     *         "attr_id_agg":{
     *           "terms": {
     *             "field": "attrs.attrId",
     *             "size": 10
     *           },
     *           "aggs": {
     *             "attr_name_agg": {
     *               "terms": {
     *                 "field": "attrs.attrName",
     *                 "size": 10
     *               }
     *             },
     *             "attr_value_agg": {
     *               "terms": {
     *                 "field": "attrs.attrValue",
     *                 "size": 10
     *               }
     *             }
     *           }
     *         }
     *       }
     *     }
     *   }
     * }
     */
    @Override
    public SearchResult search(SearchParam param) throws Exception {
        // 1.构建出dsl语句
        SearchRequest req = buildSearchRequest(param);
        // 2.发送dsl查询
        SearchResponse<SkuESModel> resp = client.search(req, SkuESModel.class);
        // 3.得出结果
        SearchResult result = buildSearchResult(param, resp);


        return result;
    }

    // 构造返回结果
    private SearchResult buildSearchResult(SearchParam param, SearchResponse<SkuESModel> resp) {
        for (Hit<SkuESModel> hit : resp.hits().hits()) {
            log.debug(String.valueOf(hit.source()));
        }
        SearchResult result = new SearchResult();
        // products
        result.setProducts(resp.hits().hits().stream().map(h->h.source()).collect(Collectors.toList()));
        // total
        long total = resp.hits().total().value();
        System.out.println("总记录数：" + total);
        Integer pageNum = param.getPageNum();
        Integer totalPages = Math.toIntExact((total % 10) == 0 ? total / 10 : total / 10 + 1);
        result.setTotal((int) total);
        result.setPageNum(pageNum);
        result.setTotalPages(totalPages);
        // aggs
        Map<String, Aggregate> aggs = resp.aggregations();
        // brands
        Aggregate brand_agg = aggs.get("brand_agg");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate)brand_agg._get();
        List<LongTermsBucket>  brandIdBuckets = longTermsAggregate.buckets().array();
        List<SearchResult.BrandVo> brandList = brandIdBuckets.stream().map(bk -> {
            // Long brandId = Long.parseLong(bk.key());
            Long brandId = bk.key();
            long docCount = bk.docCount();
            // brandName
            // AggregateVariant brand_name_agg = bk.aggregations().get("brand_name_agg")._get();
            Object brand_name_agg = bk.aggregations().get("brand_name_agg")._get();
            List<StringTermsBucket> brandNameBuckets = ((StringTermsAggregate) brand_name_agg).buckets().array();
            String brandName = brandNameBuckets.get(0).key();
            // brandImg
            Object brand_img_agg = bk.aggregations().get("brand_img_agg")._get();
            List<StringTermsBucket> brandImgBuckets = ((StringTermsAggregate) brand_img_agg).buckets().array();
            String brandImg = brandImgBuckets.get(0).key();
            return new SearchResult.BrandVo(brandId, brandName, brandImg);
        }).collect(Collectors.toList());
        System.out.println("brandList:");
        brandList.forEach(System.out::println);
        result.setBrands(brandList);
        // catalogs
        Aggregate catalog_agg = aggs.get("catalog_agg");
        LongTermsAggregate catalogIdAggregate = (LongTermsAggregate)catalog_agg._get();
        List<LongTermsBucket>  catalogIdBuckets = catalogIdAggregate.buckets().array();
        List<SearchResult.CatalogVo> catalogList = catalogIdBuckets.stream().map(bk -> {
            // Long catalogId = Long.parseLong(bk.key());
            Long catalogId = bk.key();
            long docCount = bk.docCount();
            // catalogName
            Object catalog_name_agg = bk.aggregations().get("catalog_name_agg")._get();
            List<StringTermsBucket> catalogNameBuckets = ((StringTermsAggregate) catalog_name_agg).buckets().array();
            String catalogName = catalogNameBuckets.get(0).key();
            return new SearchResult.CatalogVo(catalogId, catalogName);
        }).collect(Collectors.toList());
        System.out.println("catalogList:");
        catalogList.forEach(System.out::println);
        result.setCatalogs(catalogList);
        // attrs
        Aggregate attr_agg = aggs.get("attr_agg");
        Map<String, Aggregate> nestedAggregations = attr_agg.nested().aggregations();
        Aggregate attr_id_agg = nestedAggregations.get("attr_id_agg");
        List<LongTermsBucket> attrIdBuckets = ((LongTermsAggregate) attr_id_agg._get()).buckets().array();
        List<SearchResult.AttrVo> attrIdList = attrIdBuckets.stream().map(bk -> {
            // Long attrId = Long.parseLong(bk.key());
            Long attrId = bk.key();
            long docCount = bk.docCount();
            // attrName
            Object attr_name_agg = bk.aggregations().get("attr_name_agg")._get();
            List<StringTermsBucket> attrNameBuckets = ((StringTermsAggregate) attr_name_agg).buckets().array();
            String attrName = attrNameBuckets.get(0).key();
            // attrValue
            Object attr_value_agg = bk.aggregations().get("attr_value_agg")._get();
            List<StringTermsBucket> attrValueBuckets = ((StringTermsAggregate) attr_value_agg).buckets().array();
            List<String> attrValues = attrValueBuckets.stream().map(vbk -> vbk.key()).collect(Collectors.toList());

            return new SearchResult.AttrVo(attrId, attrName, attrValues);
        }).collect(Collectors.toList());
        System.out.println("attrIdList:");
        attrIdList.forEach(System.out::println);
        result.setAttrs(attrIdList);
        return result;
    }

    // 构造请求dsl
    private SearchRequest buildSearchRequest(SearchParam param) throws Exception {
        SearchParam p = param;
//        p.setKeyword("华为 apple 小米");
//        p.setCatelog3Id(225L);
//        p.setBrandId(Arrays.asList(9L, 10L, 11L, 12L, 15L));
//        p.setAttrs(Arrays.asList("15_以官网信息为准:高通(Qualcomm)")); // 需要处理
//        p.setHasStock(0); // 需要处理 0-false，1-true
//        p.setSkuPrice("_8000"); // 需要处理 from to
//        p.setSort("skuPrice_desc"); // 需要处理
//        p.setPageNum(1); // 需要处理 得到from和size
        // 参数处理
        SearchParam4es ps = SearchParam4es.build(p);

        Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> f = b->b;
        SearchRequest.Builder b = new SearchRequest.Builder();
        b.timeout("3m");
        b.index(EsConstant.PRODUCT_INDEX);
        BoolQuery.Builder boolQuery = QueryBuilders.bool();

        // skuTitle
        String skuTitle = ps.getKeyword();
        if(!StringUtils.isEmpty(skuTitle)) {
            boolQuery.must(QueryBuilders.match(
                    t->t.field("skuTitle").query(val->val.stringValue(skuTitle))));
        }
        // catalogId
        Long catelogId = ps.getCatelog3Id();
        if(catelogId != null) {
            boolQuery.filter(QueryBuilders.term(t->t.field("catalogId").value(v->v.longValue(catelogId))));
        }
        // brandId
        List<Long> brandIds = ps.getBrandId();
        if(brandIds != null && brandIds.size() > 0) {
            boolQuery.filter(QueryBuilders.terms(t->t.field("brandId").terms(
                            ts->ts.value(
                                    brandIds.stream().map(
                                            id-> FieldValue.of(fv->fv.longValue(id))
                                    ).collect(Collectors.toList()))
                    )
            ));
        }
        // attrs
        List<SearchParam4es.Attr> attrs = ps.getAttrList();
        if(attrs != null && attrs.size() > 0) {
//            BoolQuery attrsBool = QueryBuilders.bool().build();
            for (SearchParam4es.Attr attr : attrs) {
                BoolQuery.Builder boolQuery2 = QueryBuilders.bool().must(QueryBuilders.term(
                        tm->tm.field("attrs.attrId").value(attr.getAttrId()))).must(QueryBuilders.terms(ts->ts.field("attrs.attrValue").terms(
                        tsv->tsv.value(
                                attr.getAttrValues().stream().map(
                                        v->FieldValue.of(v)).collect(Collectors.toList())))));
                Query nested = QueryBuilders.nested(n -> n.path("attrs").query(boolQuery2.build()._toQuery()));

                boolQuery.filter(nested);
            }
        }
        // hasStock
        Boolean hasStockBool = ps.getHasStockBool();
        if(hasStockBool != null) {
            boolQuery.filter(QueryBuilders.term(t->t.field("hasStock").value(hasStockBool)));
        }
        // skuPrice range
        Float skuPriceFrom = ps.getSkuPriceFrom();
        Float skuPriceTo = ps.getSkuPriceTo();
        boolQuery.filter(QueryBuilders.range(r->{
            RangeQuery.Builder builder = r.field("skuPrice");
            if(skuPriceFrom != null) {
                builder.gte(JsonData.of(skuPriceFrom));
            }
            if(skuPriceTo != null) {
                builder.lte(JsonData.of(skuPriceTo));
            }
            return builder;
        }));
        // 设置filter
        b.query(boolQuery.build()._toQuery());

        // sort
        String sort = ps.getSort();
        if(!StringUtils.isEmpty(sort)) {
            b.sort(s->s.field(
                    o->o.field(ps.getSortField()).order(ps.getSortOrder())
            ));
        }

        // 分页
        Integer from = ps.getFrom();
        b.from(from == null ? 0 : from);
        b.size(10);

        // 高亮
        b.highlight(
                h->h.fields("skuTitle", HighlightField.of(
                                hf->hf.preTags("<b style='color:red'>").postTags("</b>")
                        )
                ));

        // 聚合
        b.aggregations("brand_agg",
                a->a.terms(
                        //  取聚合的前10条数据
                        ts->ts.field("brandId").size(10)
                ).aggregations("brand_name_agg", a2->a2.terms(
                        tm2->tm2.field("brandName")
                )).aggregations("brand_img_agg", a3->a3.terms(
                        tm2->tm2.field("brandImg")
                ))
        ).aggregations("catalog_agg",
                a2->a2.terms(
                        ts->ts.field("catalogId").size(10)
                ).aggregations("catalog_name_agg", a3->a3.terms(
                        tm2->tm2.field("catalogName")
                ))
        ).aggregations("attr_agg",
                a2->a2.nested(n->n.path("attrs"))
                        .aggregations("attr_id_agg",
                                a3->a3.terms(
                                        tm2->tm2.field("attrs.attrId").size(10)
                                ).aggregations("attr_name_agg",
                                        a4->a4.terms(tm3->tm3.field("attrs.attrName").size(10))
                                ).aggregations("attr_value_agg",
                                        a5->a5.terms(tm3->tm3.field("attrs.attrValue").size(10))
                                )
                        )
        );
        return b.build();
    }
}
